/**
 ******************************************************************************
 * @file	interface/port-linux.c
 * @brief	CPU和FPGA的SPI通信在Linux的移植，主要实在linux下写，
 * 其他平台没有弄过
 *
 * 2021.7.25 Menglong Wu,MenglongWoo@aliyun.com
 *	重新整理代码模块
 */

#include <autoconfig.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#include <sys/ioctl.h>


#include <assert.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <stdio.h>

#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdlib.h>

#include "port.h"
#include "fpga-common.h"
// #include "port.h"

/**
 * @brief	fpga 通过SPI方式传输
 * @param[in]	dev 设备描述符
 * @param[in]	cmd 发送命令
 * @param[in]	tbuf 发送内容
 * @param[in]	tlen 发送长度（不超过4000byte）
 * @param[in]	rbuf 读取内容
 * @param[in]	rlen 读取长度（不超过4000byte）
 * @param[in]   wait 发送tbuf内容后，等待多久读取rbuf，单位是us
 * @retval	null
 * @remark 调试版本中，在发送数据后固定延时5ms之后才读取数据，
    发布版可视情况删除
 */

unsigned int __fpga_transfer(struct fpga_dev *dev, unsigned char cmd, char *tbuf, int tlen, char *rbuf, int rlen, int wait)
{
    /*
    防止 struct fpga_2016 被编译器 n字节对齐，导致长度大于MIN_TX_PACK_SIZE
    末尾的 unsigned short 是校验码
    */
    memset(rbuf, 0xbb, rlen);
    assert(sizeof(struct fpga_2016) + sizeof(unsigned short) ==
           MIN_TX_PACK_SIZE);
    wdt_clear();  // 如果使用到电源管理，那么就在这里喂看门狗，

    char                    tx_head[sizeof(struct fpga_2016)];
    struct fpga_2016       *ptx     = (struct fpga_2016 *)tx_head;
    unsigned short          cksum   = 0;
    char                    unuse[] = { 0xaf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa };
    struct spi_ioc_transfer arr[]   = {
        // {
        // 	.tx_buf = (unsigned long)unuse,
        // 	.rx_buf = (unsigned long)NULL,
        // 	.len = ARRAY_SIZE(unuse),
        // 	.delay_usecs = 50000,
        // 	.cs_change = 0,
        // },
        {
		  .tx_buf      = (unsigned long)tx_head,
		  .rx_buf      = (unsigned long)NULL,
		  .len         = sizeof(struct fpga_2016),
		  .cs_change   = 0,
		  .delay_usecs = 100,
        },
        {
		  .tx_buf      = (unsigned long)tbuf,
		  .rx_buf      = (unsigned long)NULL,
		  .len         = tlen,
		  .cs_change   = 0,
		  .delay_usecs = wait,
        },
        {
		  .tx_buf      = (unsigned long)&cksum,
		  .rx_buf      = (unsigned long)NULL,
		  .len         = 2,
		  .cs_change   = 0,
		  .delay_usecs = wait,  // 发送完后给 FPGA
                                      // 响应时间，再读取
        },
        {
		  .tx_buf    = (unsigned long)NULL,
		  .rx_buf    = (unsigned long)rbuf,
		  .len       = rlen,
		  .cs_change = 1,
        },
    };
    ptx->head[0] = 'G', ptx->head[1] = 'L', ptx->head[2] = 'I',
    ptx->head[3] = 'N', ptx->head[4] = 'K';
    ptx->unuse0 = 0;
    ptx->unuse1 = 0;
    ptx->addr   = 0;
    ptx->len    = 10 + tlen;
    ptx->cmd    = cmd;
    cksum       = cmd;
    cksum += _tx_checksum((uint8_t *)tbuf, tlen);

    // 只是为了调试，启动后读取环境变量 DBG_TX 存储到env_dbg_tx
    if (env_dbg_tx) {
	printf("*****************************");
	_show_cmd_name(cmd);
	printf("tx: %d byte", sizeof(struct fpga_2016));
	PrintfMemory((uint8_t *)tx_head, sizeof(struct fpga_2016));
	printf("buf: %d byte", tlen);
	PrintfMemory((uint8_t *)tbuf, tlen);
	printf("cksum: %d byte", 2);
	PrintfMemory((uint8_t *)&cksum, 2);
    }

    /***********************************************************/
    // Linux的SPI发送方式，其他操作系统只需要修改它
    ioctl(dev->fd, SPI_IOC_MESSAGE(ARRAY_SIZE(arr)), &arr);
    /***********************************************************/

    // 只是为了调试，启动后读取环境变量 DBG_RX 存储到env_dbg_rx
    if (env_dbg_rx) {
	if (rlen > 64) {
	    rlen = 64;
	}
	// if (cmd == OTDR_IS_FINISH) {
	// 	return -1;
	// }
	PrintfMemory((uint8_t *)rbuf, rlen);
    }

    return -1;
}

#if 0  // 在其他操作系统上rt-thread上使用
static int ioctl(int fd, unsigned long request, ...);
{

}
#endif
